package com.jwkj.collector.modbus.base;

import com.jwkj.collector.modbus.util.UCRC16;

public abstract class ModbusDataGram {
    protected byte addr;//8bit
    protected byte funCode;//8bit
    protected byte[] datas;//8*Nbit
    protected byte[] check;//crc16 16bit

    @SuppressWarnings("unused")
    private ModbusDataGram() {

    }

    /**
     * 将buf解码成对象
     */
    public ModbusDataGram(byte[] buf) {
        this.addr = buf[0];
        this.funCode = buf[1];
        byte[] data = new byte[buf.length - 2 - 2];
        System.arraycopy(buf, 2, data, 0, data.length);
        this.datas = data;
        byte[] check = new byte[2];
        check[0] = buf[buf.length - 2];
        check[1] = buf[buf.length - 1];
        this.check = check;

        //获取除校验区之外的数据帧
        byte[] allData = new byte[buf.length - 2];
        System.arraycopy(buf, 0, allData, 0, buf.length - 2);
        //进行crc16校验
        byte[] crc = UCRC16.CRC_16(allData, allData.length);
        swapByte(crc);
        for (int i = 0; i < this.check.length; i++) {
            if (this.check[i] != crc[i]) {
                throw new RuntimeException("CRC16校验不通过");
            }
        }
    }

    /**
     * 构造方法,数据区和校验区由子类初始化
     */
    public ModbusDataGram(byte addr, byte funCode) {
        this.addr = addr;
        this.funCode = funCode;
    }

    /**
     * 将对象中的属性拼装成数据区<br/>
     * 不同的报文只有数据区格式不一致
     */
    protected abstract byte[] madeDatas();

    /**
     * 获取所有的数据帧数据,除了校验码.用于计算校验码
     */
    private byte[] allBytesWithNoCheck() {
        byte[] result = new byte[1 + 1 + getDatas().length];
        result[0] = addr;
        result[1] = funCode;
        System.arraycopy(getDatas(), 0, result, 2, getDatas().length);
        return result;
    }

    /**
     * 转换成对应的报文
     */
    public byte[] toBytes() {
        byte[] result = new byte[1 + 1 + getDatas().length + 2];
        result[0] = addr;
        result[1] = funCode;
        System.arraycopy(getDatas(), 0, result, 2, getDatas().length);
        result[1 + getDatas().length + 1] = check[0];
        result[1 + getDatas().length + 2] = check[1];
        return result;
    }

    /**
     * 对数据区进行CRC16计算
     */
    protected byte[] checkDataBytes() {
        byte[] buf = allBytesWithNoCheck();
        byte check[] = UCRC16.CRC_16(buf, buf.length);
        swapByte(check);//check数据区的CRC16是按[低,高]位字节存放
        this.check = check;
        return check;
    }

    /**
     * 高低字节倒转
     */
    protected void swapByte(byte[] check) {
        byte temp = check[0];
        check[0] = check[1];
        check[1] = temp;
    }

    public static ModbusFunction valueOf(String funCode) {
        int fun = Integer.valueOf(funCode);
        if (0x03 == fun) {
            return ModbusFunction.MULTI_READ;
        }
        if (0x05 == fun) {
            return ModbusFunction.ONE_WRITE;
        }
        throw new RuntimeException("不支持该方法[" + fun + "]");
    }

    //////////////getter&setter

    public byte getAddr() {
        return addr;
    }

    public void setAddr(byte addr) {
        this.addr = addr;
    }

    public byte getFunCode() {
        return funCode;
    }

    public void setFunCode(byte funCode) {
        this.funCode = funCode;
    }

    public byte[] getDatas() {
        return datas;
    }

    public void setDatas(byte[] datas) {
        this.datas = datas;
    }

    public byte[] getCheck() {
        return check;
    }

    public void setCheck(byte[] check) {
        this.check = check;
    }
    /////////////

}
